home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-03 / pbwiz16.zip / PBWIZ.DOC < prev    next >
Text File  |  1993-02-25  |  78KB  |  2,006 lines

  1.                 The PowerBASIC Wizard's Library
  2.                 =-----------------------------=
  3.                           Version 1.6
  4.  
  5.      PBWIZ  Copyright (c) 1991-1993  Thomas G. Hanlin III
  6.  
  7.  
  8.  
  9. This is the PowerBasic Wizard's Library, a collection of BASIC
  10. and assembly language routines for use with PowerBasic 3.0 by
  11. Spectra Publishing.  This collection is protected by copyright,
  12. but may be distributed according to the following conditions:
  13.  
  14.    All PBWiz files must be distributed together in unmodified
  15.    form.  No files may be removed or added.
  16.  
  17. YOU USE THIS LIBRARY AT YOUR OWN RISK.  It has been tested by
  18. me on my own computer, but I will not assume any responsibility
  19. for any problems which PBWiz may cause you.  If you encounter a
  20. problem, please let me know about it, and I will do my best to
  21. verify and repair the error.
  22.  
  23. Shareware is not free software.  It operates on a "try before
  24. you buy" basis.  It is expected that if you find PBWiz useful,
  25. you will register your copy.  You may not use PBWiz routines in
  26. programs intended for distribution unless you have registered.
  27.  
  28. Registration entitles you to receive the latest version of
  29. PBWiz, complete with full source code in assembly language and
  30. BASIC.  The assembly code is designed for the MASM 6.0
  31. assembler and may require modifications for use with other
  32. assemblers.  See the ORDER.FRM file for ordering information.
  33.  
  34. Warning: Unregistered use of PBWiz for more than 30 days may
  35. cause the author to give up computing and devote his life to
  36. Hare Krishna.  Please don't let this happen!
  37.  
  38.                        Table of Contents                 page 2
  39.  
  40.  
  41.  
  42.  Synopsis and Legal Info .................................... 1
  43.  
  44.  Table of Contents .......................................... 2
  45.  
  46.  Overview ................................................... 3
  47.  
  48.  Library Utilities .......................................... 4
  49.  
  50.  ANSI emulation ............................................. 5
  51.  
  52.  Archives ................................................... 6
  53.  
  54.  Dates and Times ............................................ 8
  55.  
  56.  Disk Directories ........................................... 9
  57.  
  58.  Equipment Info ............................................ 11
  59.  
  60.  Extended Math ............................................. 15
  61.  
  62.  Graphics .................................................. 19
  63.  
  64.  Keyboard .................................................. 25
  65.  
  66.  Memory (EMS) .............................................. 29
  67.  
  68.  Memory (XMS) .............................................. 32
  69.  
  70.  Mouse Support ............................................. 34
  71.  
  72.  Strings ................................................... 37
  73.  
  74.  Text-mode Video ........................................... 40
  75.  
  76.  Credits ................................................... 44
  77.  
  78.                            Overview                      page 3
  79.  
  80.  
  81.  
  82. Any program that uses any of the PBWiz routines must DECLARE
  83. them appropriately.  To make this easy, I've created a single
  84. file which contains all of the necessary declarations.  Put
  85. the following line at the top of your program to use it:
  86.  
  87.    $INCLUDE "pbwiz.inc"
  88.  
  89. The DECLARE statements contained in the PBWIZ.INC file tell
  90. PowerBASIC how to behave when it runs into any PBWiz routines.
  91. Unfortunately, PowerBASIC assumes you actually need all of the
  92. DECLAREd routines in your program-- so, use this only for
  93. development, until you're sure which PBWiz routines are needed
  94. by the program.  At that point, you can remove the $INCLUDE,
  95. and put in only the DECLAREs for the routines you actually use
  96. in your program.  That will make your .EXE much smaller.
  97.  
  98. The DECLAREs just tell PowerBASIC what routines to expect.  You
  99. must also tell PowerBASIC where to find the routines:
  100.  
  101.    $LINK "pbwiz.pbl"
  102.  
  103. It is no longer necessary to $LINK each unit separately, since
  104. PowerBasic 3.0 provides true libraries.  A library is nothing
  105. more or less than a collection of units.  Instead of having to
  106. remember which routine is in which unit, you just put the unit
  107. in a library, and let the linker figure it out.  Look, Ma-- no
  108. hands!  The ability to use libraries adds terrific power to the
  109. BASIC language.  But of course, you expect power of PowerBASIC!
  110.  
  111. Note that $LINK is only used in your main program.  If you
  112. write units which use PBWiz, you must include the appropriate
  113. DECLAREs in each unit, but not $LINK.  The $LINK metacommand
  114. goes in the main program which makes use of the unit, not in
  115. the unit itself.
  116.  
  117.                       Library Utilities                  page 4
  118.  
  119.  
  120.  
  121. The PBLIB.EXE librarian that comes with PowerBASIC 3.0 provides
  122. most of the features you'd expect of a library utility.  Its
  123. interactive design is convenient for light library handling.
  124. If you're using a serious library like PBWiz, however, you may
  125. find it inadequate.  PBWiz comes with a set of utilities which
  126. extend PBLIB to be more useful with large libraries.  These
  127. utilities act as a shell for PBLIB, feeding it appropriate
  128. values based on your input-- so they are perfectly compatible.
  129.  
  130. If you wish to update a library with new versions of routines
  131. that are already in the library, use LIBUPD:
  132.  
  133.    LIBUPD libname module
  134.  
  135. Here, "libname" is the name of the library.  The .PBL extension
  136. is optional.  The "module" is the name of the object file (.OBJ
  137. or .PBU) to update.  You may list multiple modules, separated
  138. by spaces, and the module name(s) may contain wildcards.
  139. LIBUPD will delete existing modules by that name (regardless of
  140. extension) from the library before adding the current version
  141. of the module to the library.  If you don't specify a module
  142. extension, LIBUPD will use .OBJ if available, or .PBU otherwise.
  143.  
  144. The LIBUPD utility can be used to add new modules to a library.
  145. However, if you are building a new library or are adding
  146. modules that you know are not already in an existing library,
  147. it is more efficient to use LIBADD, which works the same way:
  148.  
  149.    LIBADD libname module
  150.  
  151. You can also list the contents of a library in any of a number
  152. of ways, using LIBLIST:
  153.  
  154.    LIBLIST libname switch
  155.  
  156. The switch may come before or after the libname.  Only one
  157. switch may be used at a time.  The switch may be:
  158.  
  159.    /EXTERNALS      list externals (routines the module needs)
  160.    /MODULES        list modules
  161.    /PUBLICS        list publics (routines the module provides)
  162.    /RAW            list the info that PBLIB gives, unchanged
  163.  
  164. The switch may be abbreviated to as little as one character.
  165. If you choose /EXTERNALS or /PUBLICs, the result will be an
  166. alphabetized list, cross-referenced with the name of the module
  167. that contains the symbols.  If you choose /MODULES, an
  168. alphabetized list of the names of the modules contained in the
  169. library will be provided.  If you choose /RAW, you'll get the
  170. same results as you would by going directly through PBLIB.EXE.
  171. The name of the output file will be libname.LST.
  172.  
  173. These utilities create temporary files before the final output.
  174. You will get faster results if you set an environment variable
  175. TEMP to point to a RAMdisk.
  176.  
  177.                         ANSI Emulation                   page 5
  178.  
  179.  
  180.  
  181. The ANSI emulator allows you to display text with ANSI codes
  182. without any need for ANSI.SYS or other ANSI drivers.  All you
  183. have to do is replace any PRINT statements with a call to the
  184. ANSI emulator:
  185.  
  186.    PRINT St$;
  187.  
  188. turns into
  189.  
  190.    CALL AnsiPrint (St$)
  191.  
  192. Note that this does not include a carriage return and linefeed.
  193. If you want one, you'll have to add it explicitly:
  194.  
  195.    St$ = St$ + CHR$(13) + CHR$(10)
  196.    CALL AnsiPrint (St$)
  197.  
  198. Other ASCII codes are supported as well, including CHR$(7)
  199. [bell], CHR$(8) [backspace], and CHR$(12) [formfeed].
  200.  
  201. For a list of ANSI display codes, see your DOS manual or check
  202. with your local BBS.
  203.  
  204.                            Archives                      page 6
  205.  
  206.  
  207.  
  208. When I started in the microcomputer industry, there was a
  209. small variety of file archivers, all (more or less)
  210. compatible.  They did not provide compression, which was
  211. relegated to another large selection of more-or-less
  212. compatible utilities.  Then came SEA's ARC.  It was very slow,
  213. but it did compression as well as archiving, and included CRC
  214. checks so you could know whether the files were intact.  It
  215. swept the BBS scene in short order, becoming the new
  216. standard.  A few other archivers competed on about a level
  217. footing, providing only minor variances on the ARC theme. Then
  218. SEA decided to sue one of its more successful competitors, Phil
  219. Katz (PKARC).  The end result was PKZIP... but in the chaos
  220. resulting from the breaking of the ARC standard, many other
  221. archivers came into being: ARJ, LZH, PAK, ZOO, and so forth.
  222.  
  223. PBWiz helps resolve the confusion by providing a single set of
  224. routines which allow you to view the contents of archives in
  225. any of the above-mentioned formats: ARC, ARJ, LZH, PAK, ZIP, or
  226. ZOO.  It also handles self-extracting EXE files of the forms
  227. produced by LHARC and ZIP2EXE.  Only archive directories are
  228. provided at this time.  Other formats will also be added as
  229. they arise.  If you have details on the format of an archive
  230. that you'd like me to add to PBWiz, please send them my way.
  231.  
  232. Viewing archive directories is handled in roughly the same
  233. fashion as you might view a DOS file directory.  This makes it
  234. possible to treat an archive and a subdirectory in a similar
  235. manner.
  236.  
  237. When you're looking for the first file in an archive, use the
  238. FindFirstA function.  You must specify the archive name and a
  239. file name.  The archive name may include a drive and path
  240. specification, and does not need to have the archive
  241. extension.  If you leave off the extension, FindFirstA will use
  242. the first archive it comes across that matches the rest of the
  243. specification. Note that the archive specification may not
  244. contain wildcards.  In contrast, the search file name may not
  245. contain drive or path specs, but may contain wildcards.
  246.  
  247.    CALL FindFirstA (Archive$, Filename$, ErrCode%)
  248.  
  249.                            Archives                      page 7
  250.  
  251.  
  252.  
  253. If there are no files to be found, or if the archive
  254. specification was bad, an error code will be returned.  If
  255. there was no error, there may well be more files to be found.
  256. You can find each of them with FindNextA:
  257.  
  258.    CALL FindNextA (ErrCode%)
  259.  
  260. Of course, just finding a matching file doesn't do you much
  261. good unless you can retrieve information about it.  You can use
  262. any of the following routines to provide information about a
  263. matched file:
  264.  
  265.    Nam$ = GetNameA$
  266.    Dat$ = GetDateA$
  267.    Tim$ = GetTimeA$
  268.    CRC$ = GetCRCA$
  269.    StorageMethod$ = GetStoreA$
  270.  
  271.    CALL GetSizeA (OriginalSize&, CurrentSize&)
  272.  
  273. When you're done viewing an archive, be sure to close it:
  274.  
  275.    CALL CloseA
  276.  
  277. Let's try an example, to view all files in an archive:
  278.  
  279.    $INCLUDE "pbwiz.inc"
  280.    $LINK "pbwiz.pbl"
  281.    ' set Archive$ to the name of an archive here!
  282.    CALL FindFirstA (Archive$, "*.*", ErrCode%)
  283.    DO UNTIL ErrCode%
  284.       PRINT GetNameA$
  285.       CALL FindNextA (ErrCode%)
  286.    LOOP
  287.    CALL FCloseA
  288.  
  289. This program fragment assumes that you have set Archive$ to the
  290. name of an archive.  It might be convenient to set it to the
  291. command line for testing purposes:
  292.  
  293.    Archive$ = UCASE$(LTRIM$(RTRIM$(COMMAND$)))
  294.  
  295.                         Dates and Times                  page 8
  296.  
  297.  
  298.  
  299. This unit allows you to validate and compare dates.  It also
  300. provides the day of the week, given the date.  Dates may not be
  301. before the year 1900.  Date strings may be in the form
  302. "01/01/91" or "01-01-1991" (the delimiter is not significant
  303. and years may be two or four digits; two-digit years will be
  304. assumed to be in the 20th century).
  305.  
  306. Let's start off with date validation.  It's often important to
  307. know if a date entered into your program is a valid date.
  308.  
  309.    IF GoodDate%(DateSt$) THEN PRINT "The date is valid."
  310.  
  311. It can also be helpful to know on which day of the week a given
  312. date falls.
  313.  
  314.    Day$ = WeekDay$(DateSt$)
  315.  
  316. There are many useful things you can accomplish by turning a
  317. date into a number which represents that date (or vice versa).
  318. This allows you to compare two dates, which is important if you
  319. want to sort by date; find out what the date will be in a given
  320. number of days, or what it was some number of days ago; find
  321. the number of days between two dates; display a calendar; and
  322. so forth.  This is easy to do with PBWiz:
  323.  
  324.    DateNr& = Date2Num&(DateSt$)
  325.  
  326.    DateSt$ = Num2Date$(DateNr&)
  327.  
  328. The DateNr& represents the number of days since January 1,
  329. 1900.  This is less than 65,535 for dates that go up to around
  330. the year 2070 or so, so you may wish to store the dates in
  331. compressed two-byte form if your required range of dates is not
  332. that large:
  333.  
  334.    CrunchDate% = CVI(LEFT$(MKL$(DateNr&), 2))
  335.  
  336. This can be reversed simply:
  337.  
  338.    DateNr& = CVL(MKI$(CrunchDate%) + STRING$(2, 0))
  339.  
  340. Note that dates crunched this way are only useful for storage
  341. purposes, since the numbers greater than 32,767 are stored as
  342. negative numbers due to the signed integer format BASIC uses.
  343. You must uncompress them before doing any comparisons or date
  344. calculations.  Still, for a savings of 50%, it may be worth the
  345. hassle to convert back and forth.
  346.  
  347.                        Disk Directories                  page 9
  348.  
  349.  
  350.  
  351. This unit lets you read disk directories and retrieve the same
  352. information the DIR command shows, plus the file attribute.  A
  353. string buffer is used to allow recursive directory searching.
  354.  
  355. When you're looking for the first file in a directory, use the
  356. FindFirstF function.  You must provide a search filespec, which
  357. may contain a drive and path specification and use wildcards.
  358. You must also provide a search attribute, which may be any
  359. combination of the following added together:
  360.  
  361.    Normal          0      (nothing special)
  362.    Read Only       1      file can be read, but not written to
  363.    Hidden          2      file is "invisible"
  364.    System          4      special DOS system file
  365.    Subdirectory   16      subdirectory
  366.    Archive        32      (used by some backup utilities)
  367.  
  368. Note that you will always get all files that match any of your
  369. search specs.  For example, if your search attribute was 18,
  370. you'd get normal files, hidden files, normal subdirectories and
  371. hidden subdirectories.  If you want to be more specific, you
  372. will have to test the file attribute of the resulting file.
  373.  
  374. You must provide a string buffer for the directory search
  375. routine.  This buffer must be 64 characters long and should be
  376. initialized before each call to FindFirstF.  By using different
  377. buffer strings, you can search more than one directory at a
  378. time, or perform recursive searches through directories.
  379.  
  380.    Buffer$ = SPACE$(64)
  381.    CALL FindFirstF (Buffer$, Filename$, ErrCode%)
  382.  
  383. If there are no files to be found, or if the file specification
  384. was bad, an error code will be returned.  If there was no
  385. error, there may well be more files to be found.  You can find
  386. each of them with FindNextF:
  387.  
  388.    CALL FindNextF (Buffer$, ErrCode%)
  389.  
  390. Of course, just finding a matching file doesn't do you much
  391. good unless you can retrieve information about it.  You can use
  392. any of the following routines to provide information about a
  393. matched file:
  394.  
  395.    FilAttr% = GetAttrF%(Buffer$)       ' file attribute
  396.    FilName$ = GetNameF$(Buffer$)       ' file name
  397.    FilDate$ = GetDateF$(Buffer$)       ' file date
  398.    FilSize& = GetSizeF&(Buffer$)       ' file size
  399.    FilTime$ = GetTimeF$(Buffer$)       ' file time
  400.  
  401.                        Disk Directories                 page 10
  402.  
  403.  
  404.  
  405. The file attribute can be most readily decoded with a series of
  406. ANDs.  For example, to test for a subdirectory, you'd use:
  407.  
  408.    IF FilAttr% AND 16 THEN PRINT "subdirectory"
  409.  
  410. Let's put all these routines together and see what it takes to
  411. make a quick'n'dirty DIR-style utility.
  412.  
  413.    $INCLUDE "pbwiz.inc"
  414.    $LINK "pbwiz.pbl"
  415.    SearchName$ = COMMAND$  ' get search spec from command line
  416.    Buffer$ = SPACE$(64)    ' set up buffer
  417.    CALL FindFirstF(Buffer$, SearchName$, SearchAttr%, ErrCode%)
  418.    DO UNTIL ErrCode%
  419.       PRINT GetNameF$(Buffer$), GetSizeF&(Buffer$),
  420.       PRINT GetDateF$(Buffer$), GetTimeF$(Buffer$),
  421.       FilAttr% = GetAttrF%(Buffer$)
  422.       IF FilAttr% AND 1 THEN PRINT "Read Only  ";
  423.       IF FilAttr% AND 2 THEN PRINT "Hidden  ";
  424.       IF FilAttr% AND 4 THEN PRINT "System  ";
  425.       IF FilAttr% AND 16 THEN PRINT "Subdirectory  ";
  426.       IF FilAttr% AND 32 THEN PRINT "Backup  ";
  427.       PRINT
  428.       CALL FindNextA (ErrCode%)
  429.    LOOP
  430.  
  431. Doesn't take much, does it?  Now you can add disk directory
  432. handling to your program with a bare minimum of effort!
  433.  
  434.                         Equipment Info                  page 11
  435.  
  436.  
  437.  
  438. The equipment unit gives you information about the computing
  439. environment. This includes both installed software and
  440. hardware.
  441.  
  442. The first function allows you to determine if an "enhanced"
  443. keyboard (101-key) is installed.  It may not be able to figure
  444. out what the keyboard is on some older not-quite-clone PCs, in
  445. which case it will take the safe way out and report that there
  446. is no enhanced keyboard.  This function returns -1 if there is
  447. an enhanced keyboard present, 0 if not.
  448.  
  449.    Enhanced% = KbdType%
  450.  
  451. Want to know the type of processor (CPU) being used?  Can do!
  452.  
  453.    CPU% = Processor%
  454.  
  455. The results will be reported as a number which can be decoded
  456. as follows:
  457.  
  458.    0    NEC V20
  459.    1    8088 or 8086
  460.    2    80186
  461.    3    80286
  462.    4    80386
  463.    5    80486
  464.  
  465. Maybe you'd like to check for a CD-ROM drive:
  466.  
  467.    Drives% = CDROM%
  468.  
  469. This tells you how many logical drives exist, if there is a
  470. CD-ROM available. If not, it will return 0.  Note that the
  471. CD-ROM installation check conflicts with the GRAPHICS.COM
  472. installation check for DOS 4.0, due to some screw-up at IBM or
  473. Microsoft.  I'm not yet sure whether DOS 5.0 is similarly
  474. afflicted.
  475.  
  476. The number of floppy drives installed is retrieved like this:
  477.  
  478.    Drives% = Floppies%
  479.  
  480.                         Equipment Info                  page 12
  481.  
  482.  
  483.  
  484. There may be up to four floppy drives in a system; however, the
  485. AT CMOS data area only directly supports two. This makes it
  486. easy to find out what kind of drives the first two are, but not
  487. the second two.  Oh well, guess we'll have to settle for what
  488. we can get, right?
  489.  
  490.    CALL FloppyType (Drive1, Drive2)
  491.  
  492. The results from FloppyType are returned as follows:
  493.  
  494.    0    no drive
  495.    1    5 1/4"    360K
  496.    2    5 1/4"    1.2M
  497.    3    3 1/2"    720K
  498.    4    3 1/2"    1.44M
  499.  
  500. Result codes of 5-7 are available, but not yet defined.  One
  501. might guess that the 2.88M drive supported by DOS 5.0 will be
  502. drive type 5.  Has anybody seen one of those puppies yet?
  503.  
  504. New memory types sure have burgeoned over the years...
  505. expanded, extended, and now XMS.  There are routines to check
  506. all of these:
  507.  
  508.    BaseExt& = AllExtMem&     ' extended memory installed
  509.    NowExt& = GetExtM&        ' BIOS extended memory available
  510.  
  511.    CALL GetEMSm (TotalPages%, FreePages%)    ' expanded memory
  512.  
  513.    CALL GetXMSm (LargestFree&, TotalFree&)   ' XMS memory
  514.  
  515. When you're dealing with extended memory, whether it be
  516. BIOS-type or using the XMS standard, the results are returned
  517. in kilobytes.  Multiply 'em by 1024 to convert to bytes.  When
  518. you're dealing with expanded memory (EMS), the results are in
  519. pages of 16,384 bytes.
  520.  
  521. I might note, by the way, that Microsoft seems to have
  522. intentionally crippled the XMS standard.  It can only support a
  523. maximum of 64 megabytes.  This may seem like a lot now, but I
  524. can remember when my first PC (a Compaq portable) was the awe
  525. of the neighborhood with 384K RAM!  A few years down the road,
  526. the artificial limitations of XMS are going to be a nuisance.
  527.  
  528. A few more routines to get the versions of the EMS and XMS
  529. drivers, if any:
  530.  
  531.    CALL GetEMSv (MajorV%, MinorV%)
  532.    CALL GetXMSv (MajorV%, MinorV%)
  533.  
  534. These return the major and minor version numbers as two
  535. separate integers. For example, EMS 4.0 would return major
  536. version 4, minor version 0.
  537.  
  538.                         Equipment Info                  page 13
  539.  
  540.  
  541.  
  542. It's nice to know a little about the operating environment.
  543. With the below routines, you can find out what the DOS version
  544. is; what version of 4DOS, if any, is in use; and whether
  545. Microsoft Windows is running.
  546.  
  547.    CALL GetDOSv (MajorV%, MinorV%)
  548.    CALL Get4DOSv (MajorV%, MinorV%)
  549.    CALL WinCheck (MajorV%, MinorV%)
  550.  
  551. These return results as major and minor version numbers, as
  552. discussed on the previous page.  The Get4DOSv and WinCheck
  553. routines return zeroes if 4DOS and Windows, respectively, are
  554. not available.
  555.  
  556. There are a couple of curious features of GetDOSv to keep in
  557. mind.  If the version is 10 or higher, you're running in OS/2
  558. compatibility mode.  DOS version 10 is actually OS/2 1.0,
  559. version 20 is OS/2 2.0, and so on. Secondly, if you're using
  560. DOS 5.0, the version reported may not be 5.0-- DOS 5.0 can be
  561. told to reply with a lower version number to allow some older
  562. software (which checks for a specific DOS version) to run
  563. properly.
  564.  
  565. One final routine that should be of some value is the one that
  566. allows you to find out what kind of display is available.  It
  567. tells you the specific adapter and whether the display is color
  568. or monochrome.  There is one case in which it can be confused,
  569. however-- if the adapter is CGA, the display is assumed to be
  570. color, since there is no way for the computer to know any
  571. differently.  So, although this routine provides a good idea of
  572. what is available, it would be a good idea to provide an option
  573. to tell the program that a monochrome display is attached.
  574. Microsoft normally uses "/B" for this purpose, so that might be
  575. a good standard to stick with.
  576.  
  577.    CALL GetDisplay (Adapter%, Mono%)
  578.    IF Mono% THEN
  579.       PRINT "Monochrome monitor"
  580.    ELSE
  581.       PRINT "Color monitor"
  582.    END IF
  583.    SELECT CASE Adapter%
  584.       CASE 1: PRINT "MDA"
  585.       CASE 2: PRINT "Hercules"
  586.       CASE 3: PRINT "CGA"
  587.       CASE 4: PRINT "EGA"
  588.       CASE 5: PRINT "MCGA"
  589.       CASE 6: PRINT "VGA"
  590.    END SELECT
  591.  
  592.                         Equipment Info                  page 14
  593.  
  594.  
  595.  
  596. Aside from some of the oldest semi-clones, it's possible to
  597. find out what sort of machine you're using by looking at a
  598. specific data byte.  You can access this as follows:
  599.  
  600.    Machine% = PCType%
  601.  
  602. The result will need decoding.  Here are some known values:
  603.  
  604.    255   PC or XT
  605.    254   XT
  606.    253   PCjr
  607.    252   PC AT
  608.    251   XT
  609.    250   PS/2 Model 30
  610.    249   PC Convertible
  611.    248   PS/2 Model 70 or 80
  612.    154   Compaq Portable
  613.     45   Compaq Portable
  614.  
  615. Likewise, all but some of the oldest semi-clones maintain a
  616. BIOS date value which tells you how old the BIOS ROM is.  This
  617. can be retrieved with the following routine:
  618.  
  619.    BIOSdate$ = PCDate$
  620.  
  621. If your program is running on one of the rare old machines
  622. which don't maintain a valid BIOS date, this routine will
  623. return "No Date " instead of an actual date.
  624.  
  625. As far as a program is concerned, DR DOS is essentially the
  626. same as MS-DOS.  Still, it's always nice to know what sort of
  627. operating environment you have.  You can find out whether your
  628. program is running under DR DOS with the following function:
  629.  
  630.    IF DRDOS% THEN PRINT "It's DR DOS" ELSE PRINT "MS-DOS"
  631.  
  632. The number of serial and parallel ports available can be
  633. readily obtained:
  634.  
  635.    PRINT "Serial ports: "; CommPorts%
  636.    PRINT "Parallel ports: "; PrtPorts%
  637.  
  638.                          Extended Math                  page 15
  639.  
  640.  
  641.  
  642. The extended math unit provides an expression evaluator and
  643. extensions to BASIC's math.  The math extensions include
  644. hyperbolic and inverse trig functions, a few handy constants,
  645. conversions, and more.
  646.  
  647. The expression evaluator allows you to find the result of an
  648. expression contained in a string.  Normal algebraic precedence
  649. is used, e.g. 4+3*5 evaluates to 19.  The usual numeric
  650. operators (*, /, +, -, ^) are supported (multiply, divide, add,
  651. subtract, and raise to a power).  Use of negative numbers is
  652. just fine, of course.  Parentheses for overriding the default
  653. order of operations are also supported.
  654.  
  655. You may use either a double asterisk ("**") or a caret ("^")
  656. symbols to indicate exponentiation.
  657.  
  658. The constant PI is recognized, as are the following functions:
  659.    ABS    absolute value        INT    integer
  660.    ACOS   inverse cosine        LOG    natural log
  661.    ASIN   inverse sine          SIN    sine
  662.    ATAN   inverse tangent       SQR    square root
  663.    COS    cosine                TAN    tangent
  664.    FRAC   fraction
  665.  
  666. Trig functions expect angles in radians.
  667.  
  668. To evaluate an expression, you pass it to the evaluator as a
  669. string.  You will get back either an error code or a
  670. single-precision result.  Try this example to see how the
  671. expression evaluator works:
  672.  
  673.    $STACK 8192
  674.    $INCLUDE "pbwiz.inc"
  675.    $LINK "pbwiz.pbl"
  676.    DO
  677.       INPUT "Expression? "; Expr$
  678.       IF LEN(Expr$) THEN
  679.          CALL Evaluate (Expr$, Result!, ErrCode%)
  680.          IF ErrCode% THEN
  681.             PRINT "Invalid expression.  Error = "; ErrCode%
  682.          ELSE
  683.             PRINT "Result: "; Result!
  684.          END IF
  685.       END IF
  686.    LOOP WHILE LEN(Expr$)
  687.    END
  688.  
  689. The expression evaluator uses recursion and requires more than
  690. the default amount of stack space, which is why the $STACK
  691. metacommand is used.  See CALC.BAS for a working demo.
  692.  
  693.                          Extended Math                  page 16
  694.  
  695.  
  696.  
  697. The new math functions are pretty much self-explanatory, so
  698. I'll just list them here.  General notes on assumptions and
  699. constraints are given at the end of this chapter.
  700.  
  701.    Result! = ArcCosHS!(Nr!)       ' inverse hyperbolic cosine
  702.    Result! = ArcSinHS!(Nr!)       ' inverse hyperbolic sine
  703.    Result! = ArcTanHS!(Nr!)       ' inverse hyperbolic tangent
  704.    Result! = ArcCosS!(Nr!)        ' arc cosine (1 >= Nr >= -1)
  705.    Result! = ArcCotS!(Nr!)        ' inverse cotangent
  706.    Result! = ArcCotHS!(Nr!)       ' arc hyperbolic cotangent
  707.    Result! = ArcCscS!(Nr!)        ' inverse cosecant
  708.    Result! = ArcCscHS!(Nr!)       ' inverse hyperbolic cosecant
  709.    Result! = ArcSecS!(Nr!)        ' inverse secant
  710.    Result! = ArcSecHS!(Nr!)       ' inverse hyperbolic secant
  711.    Result! = ArcSinS!(Nr!)        ' arc sine   (1 >= Nr >= -1)
  712.    Result! = CeilS!(Nr!)          ' smallest integer >= Nr
  713.    Result! = Cent2Fahr!(Nr!)      ' centigrade to Fahrenheit
  714.    Result! = CosHS!(Nr!)          ' hyperbolic cosine
  715.    Result! = CotHS!(Nr!)          ' hyperbolic cotangent
  716.    Result! = CotS!(Nr!)           ' cotangent
  717.    Result! = CscHS!(Nr!)          ' hyperbolic cosecant
  718.    Result! = CscS!(Nr!)           ' cosecant
  719.    Result! = Deg2RadS!(Nr!)       ' convert degrees to radians
  720.    e! = eS!                       ' the constant "e"
  721.    Result! = ErfS!(Nr!)           ' error function
  722.    Result! = FactS!(Nr%)          ' factorial
  723.    Result! = Fahr2Cent!(Nr!)      ' Fahrenheit to centigrade
  724.    Result! = FloorS!(Nr!)         ' largest integer <= Nr
  725.    Result! = Kg2Pound!(Nr!)       ' convert kilograms to pounds
  726.    Pi! = PiS!                     ' the constant "pi"
  727.    Result! = Pound2Kg!(Nr!)       ' convert pounds to kilograms
  728.    Result! = Rad2DegS!(Nr!)       ' convert radians to degrees
  729.    Result! = SecHS!(Nr!)          ' hyperbolic secant
  730.    Result! = SecS!(Nr!)           ' secant
  731.    Result! = SinHS!(Nr!)          ' hyperbolic sine
  732.    Result! = TanHS!(Nr!)          ' hyperbolic tangent
  733.  
  734.                          Extended Math                  page 17
  735.  
  736.  
  737.  
  738.    Result# = ArcCosD#(Nr#)        ' arc cosine  (1 >= Nr >= -1)
  739.    Result# = ArcCosHD#(Nr#)       ' inverse hyperbolic cosine
  740.    Result# = ArcCotD#(Nr#)        ' inverse cotangent
  741.    Result# = ArcCotHD#(Nr#)       ' arc hyperbolic cotangent
  742.    Result# = ArcCscD#(Nr#)        ' inverse cosecant
  743.    Result# = ArcCscHD#(Nr#)       ' inverse hyperbolic cosecant
  744.    Result# = ArcSecD#(Nr#)        ' inverse secant
  745.    Result# = ArcSecHD#(Nr#)       ' inverse hyperbolic secant
  746.    Result# = ArcSinD#(Nr#)        ' arc sine    (1 >= Nr >= -1)
  747.    Result# = ArcSinHD#(Nr#)       ' inverse hyperbolic sine
  748.    Result# = ArcTanHD#(Nr#)       ' inverse hyperbolic tangent
  749.    Result# = CeilD#(Nr#)          ' smallest integer >= Nr
  750.    Result# = CosHD#(Nr#)          ' hyperbolic cosine
  751.    Result# = CotD#(Nr#)           ' cotangent
  752.    Result# = CotHD#(Nr#)          ' hyperbolic cotangent
  753.    Result# = CscD#(Nr#)           ' cosecant
  754.    Result# = CscHD#(Nr#)          ' hyperbolic cosecant
  755.    Result# = Deg2RadD#(Nr#)       ' convert degrees to radians
  756.    e# = eD#                       ' the constant "e"
  757.    Result# = ErfD#(Nr#)           ' error function
  758.    Result# = FactD#(Nr%)          ' factorial
  759.    Result# = FloorD#(Nr#)         ' largest integer <= Nr
  760.    Pi# = PiD#                     ' the constant "pi"
  761.    Result# = Rad2DegD#(Nr#)       ' convert radians to degrees
  762.    Result# = SecD#(Nr#)           ' secant
  763.    Result# = SecHD#(Nr#)          ' hyperbolic secant
  764.    Result# = SinHD#(Nr#)          ' hyperbolic sine
  765.    Result# = TanHD#(Nr#)          ' hyperbolic tangent
  766.  
  767.                          Extended Math                  page 18
  768.  
  769.  
  770.  
  771.    Result% = GCDI%(Nr1%, Nr2%)    ' greatest common denominator
  772.    Result% = RotateL%(Nr%, Count%) ' rotate left
  773.    Result% = RotateR%(Nr%, Count%) ' rotate right
  774.    Result% = ShiftL%(Nr%, Count%) ' shift left
  775.    Result% = ShiftR%(Nr%, Count%) ' shift right
  776.  
  777.    Result& = GCDL&(Nr1&, Nr2&)    ' greatest common denominator
  778.    Result& = RotateLL&(Nr&, Count%) ' rotate left
  779.    Result& = RotateRL&(Nr&, Count%) ' rotate right
  780.    Result& = ShiftLL&(Nr&, Count%) ' shift left
  781.    Result& = ShiftRL&(Nr&, Count%) ' shift right
  782.  
  783. The ceiling and floor functions are generally used in rounding.
  784.  
  785. Like BASIC's trig functions, the PBWiz trig functions expect
  786. the angle to be in radians.  Conversion functions are provided
  787. in case you prefer degrees.
  788.  
  789. "Arc" and "inverse" are used interchangeably (e.g., an arc sine
  790. is the same as an inverse sine).
  791.  
  792. Note that there is no ArcTanS! or ArcTanD# function for the
  793. simple reason that BASIC supplies an ATN function.
  794.  
  795. Constants are expressed to the maximum precision available.
  796.  
  797. If you are not familiar with variable postfix symbols, here's a
  798. brief summary:
  799.  
  800.    Symbol   Meaning             Range (approximate)
  801.    ------   --------            -------------------------------
  802.      %      integer             +- 32767
  803.      &      long integer        +- 2 * 10^9
  804.      !      single precision    +- 1 * 10^38
  805.      #      double precision    +- 1 * 10^308
  806.  
  807. See PowerBASIC's online help for further details.
  808.  
  809.                        Graphics Support                 page 19
  810.  
  811.  
  812.  
  813. I was rather surprised to find that PowerBASIC lacks support
  814. for one of the standard VGA modes-- SCREEN 13, the 320x200
  815. 256-color mode.  I immediately decided to add support for that
  816. mode: dots, lines, boxes, polygons, and (of course) text.
  817. While I was at it, I thought I'd add support for a nonstandard
  818. VGA mode: 360x480 in 256 colors.  This mode will work on almost
  819. any plain VGA system, although it might not work on some older
  820. not-quite-compatible setups.  SuperVGA modes are also supported
  821. for adapters based on the popular Tseng 4000 chip set and for
  822. any VESA-compatible SVGA.
  823.  
  824.    13    320x200, 256 colors, 40x25 text, any VGA
  825.    N0    360x480, 256 colors, 45x60 text, almost any VGA
  826.    N5     ? x ? , 256 colors, ? x ? text, Tseng 4000 SVGA
  827.    GV     ? x ?,  ??? colors, ? x ? text, VESA compatibles
  828.  
  829. The graphics routines all use the same nomenclature: a G,
  830. followed by a mode number, followed by the specific name.  This
  831. generic naming convention is used so that this chapter can
  832. refer to all available modes.  For example, if I say "G#Color"
  833. and you're using mode 13, you'd actually use "G13Color" in your
  834. program.  Ok?
  835.  
  836. There are some differences in how the various sets of graphics
  837. routines need to be initialized and shut down.  I'll give you
  838. an outline of the differences here and follow it up with more
  839. detailed information in the case of the SVGA modes.  The VESA
  840. SVGA mode, GV, should be handled like this:
  841.  
  842.    CALL GGVInit              ' initialize VESA handler
  843.    CALL GGVMode (ModeNr%)    ' enter graphics mode
  844.    ' *** your main program goes here ***
  845.    CALL GGVDone              ' exit graphics mode
  846.  
  847. The Tseng 4000 SVGA mode, N5, also needs initialization.  Its
  848. handling is closer to that of the other modes, however:
  849.  
  850.    CALL GN5Init (ModeNr%, PixelsWide%, PixelsHigh%)
  851.    CALL GN5Mode (1)          ' enter graphics mode
  852.    ' *** your main program goes here ***
  853.    CALL GN5Mode (0)          ' exit graphics mode
  854.  
  855. For the other modes, no special initialization is required.
  856. You just use G#Mode with a non-zero value to enter graphics
  857. mode, or zero to restore text mode.  The text mode is assumed
  858. to be the normal 80x25 color mode.  If this would not be your
  859. choice, you can safely use the PowerBasic SCREEN statement to
  860. pick a mode that's more to your liking.
  861.  
  862.                        Graphics Support                 page 20
  863.  
  864.  
  865.  
  866. Here's an example for typical handling of one of the non-SVGA
  867. modes (in this case, mode 13):
  868.  
  869.    CALL G13Mode (1)          ' enter graphics mode
  870.    ' *** your main program goes here ***
  871.    CALL G13Mode (0)          ' exit graphics mode
  872.  
  873. The non-SVGA modes have their actual mode number built into the
  874. routines themselves.  In the case of SVGA modes, the routines
  875. were designed for more flexibility, since SVGAs come in a wide
  876. variety of configurations.
  877.  
  878. In the case of the Tseng 4000 modes, N5, you specify the BIOS
  879. mode number, along with the screen resolution, when setting up
  880. the routines.  Only 256-color modes are supported.  The BIOS
  881. mode, width, height, and amount of video memory required to
  882. support the mode may be any of the following:
  883.  
  884.       &H2D     640 x 350    256k
  885.       &H2E     640 x 480    512k
  886.       &H2F     640 x 400    256k
  887.       &H30     800 x 600    512k
  888.       &H38    1024 x 768     1M
  889.  
  890. In the case of the VESA modes, GV, you specify the VESA mode
  891. number when setting up the routines.  To provide the greatest
  892. amount of flexibility, VESA modes are not predefined.  Rather
  893. than picking a specific mode number, you ask VESA which modes
  894. are available on the current computer, and pick the mode that
  895. best suits your needs from the choices offered.
  896.  
  897. First, it's wise to make sure VESA support is available:
  898.  
  899.    CALL VesaVersion (MajorV%, MinorV%)
  900.  
  901. This routine returns the version number of the VESA driver,
  902. split into major and minor parts.  If VESA isn't available,
  903. both numbers will be zero.
  904.  
  905. Determining which VESA modes are available works something like
  906. scanning a disk or archive directory.  You start by requesting
  907. the first mode.  Any of a variety of functions can be used to
  908. find out the mode resolution and colors, whether it's a text or
  909. monochrome mode, and so forth.  If the mode is unsuitable or
  910. you'd like to check further, you request the next mode number.
  911. It will return a mode of -1 if there are no more modes.
  912.  
  913. A working demonstration of these routines is presented on the
  914. next page, and may also be found in the file VESAINFO.BAS.
  915.  
  916.                        Graphics Support                 page 21
  917.  
  918.  
  919.  
  920.    $INCLUDE "pbwiz.inc"
  921.    $LINK "pbwiz.pbl"
  922.    DEFINT A-Z
  923.  
  924.    CALL VesaVersion (MajorV, MinorV)
  925.    IF MajorV = 0 AND MinorV = 0 THEN
  926.       PRINT "Sorry, but you do not have VESA support."
  927.       END
  928.    END IF
  929.  
  930.    VMode = VesaFindFirst
  931.    DO UNTIL VMode = -1
  932.       CLS
  933.       PRINT "VESA Mode : &H"; HEX$(VMode)
  934.       PRINT "Mode Type : ";
  935.       IF VesaIsText THEN PRINT "Text "; ELSE PRINT "Graphics ";
  936.       IF VesaIsMono THEN PRINT "(mono)" ELSE PRINT "(color)"
  937.       PRINT "Resolution:"; VesaScrWidth; "x"; VesaScrHeight;
  938.       IF VesaIsText THEN PRINT "chars" ELSE PRINT "pixels"
  939.       PRINT "Char Size :"; VesaChrWidth; "x"; VesaChrHeight
  940.       PRINT "Colors    :"; VesaColors&
  941.       PRINT
  942.       PRINT "-- press a key to view next available mode --"
  943.       VMode = VesaFindNext
  944.       DO
  945.       LOOP WHILE LEN(INKEY$)
  946.       DO
  947.          ky$ = INKEY$
  948.       LOOP UNTIL LEN(ky$)
  949.       IF ky$ = CHR$(27) OR ky$ = CHR$(3) THEN EXIT DO
  950.    LOOP
  951.    END
  952.  
  953.  
  954.  
  955. The VESA mode information functions can be summarized, so:
  956.  
  957.    VesaFindFirst%       get first available mode number
  958.    VesaFindNext%        get next available mode number
  959.  
  960.    VesaIsMono%          whether mode is mono (-1 yes, 0 no)
  961.    VesaIsText%          whether mode is text (-1 yes, 0 no)
  962.  
  963.    VesaScrHeight%       screen height (characters or pixels)
  964.    VesaScrWidth%        screen width (characters or pixels)
  965.  
  966.    VesaChrHeight%       character font height (pixels)
  967.    VesaChrWidth%        character font width (pixels)
  968.  
  969.    VesaColors&          colors (color numbers) in mode
  970.  
  971.                        Graphics Support                 page 22
  972.  
  973.  
  974.  
  975. One difference between BASIC and BasWiz is that, instead of
  976. each "draw" command requiring a color parameter as in BASIC,
  977. the PBWiz library provides a separate color command:
  978.  
  979.    CALL G#Color (Foreground%, Background%)
  980.  
  981. The "foreground" color is used by all graphics routines.  The
  982. background color is used by the G#Cls routine.  Both foreground
  983. and background colors are used in by G#Write and G#WriteLn.
  984.  
  985. Here is a list of the corresponding routines, first BASIC, then
  986. PBWiz (replace the "#" with the appropriate mode number):
  987.  
  988.    ' get the color of a specified point
  989.    colour% = POINT(x%, y%)
  990.    colour% = G#GetPel%(x%, y%)
  991.  
  992.    ' set the color of a specified point
  993.    PSET (x%, y%), colour%
  994.    CALL G#Color (colour%, backgnd%): CALL G#Plot (x%, y%)
  995.  
  996.    ' draw a line of a specified color
  997.    LINE (x1%, y1%) - (x2%, y2%), colour%
  998.    CALL G#Color (colour%, backgnd%)
  999.    CALL G#Line (x1%, y1%, x2%, y2%)
  1000.  
  1001.    ' draw a box frame of a specified color
  1002.    LINE (x1%, y1%) - (x2%, y2%), colour%, B
  1003.    CALL G#Color (colour%, backgnd%)
  1004.    CALL G#Box (x1%, y1%, x2%, y2%, 0)
  1005.  
  1006.    ' draw a box of a specified color and fill it in
  1007.    LINE (x1%, y1%) - (x2%, y2%), colour%, BF
  1008.    CALL G#Color (colour%, backgnd%)
  1009.    CALL G#Box (x1%, y1%, x2%, y2%, 1)
  1010.  
  1011.    ' clear the screen and home the cursor
  1012.    CLS
  1013.    CALL G#Cls
  1014.  
  1015.    ' get the current cursor position
  1016.    Row% = CSRLIN: Column% = POS(0)
  1017.    CALL G#GetLocate (Row%, Column%)
  1018.  
  1019.    ' set the current cursor position
  1020.    LOCATE Row%, Column%
  1021.    CALL G#Locate (Row%, Column%)
  1022.  
  1023.    ' display a string without a carriage return and linefeed
  1024.    PRINT St$;
  1025.    CALL G#Write (St$)
  1026.  
  1027.    ' display a string with a carriage return and linefeed
  1028.    PRINT St$
  1029.    CALL G#WriteLn (St$)
  1030.  
  1031.                        Graphics Support                 page 23
  1032.  
  1033.  
  1034.  
  1035. If you need to print a number rather than a string, just use
  1036. the BASIC function STR$ to convert it.  If you don't want a
  1037. leading space, use this approach:
  1038.  
  1039.    St$ = LTRIM$(STR$(Number))
  1040.  
  1041. The PBWiz library has other routines which have no BASIC
  1042. equivalent.  One allows you to get the current colors:
  1043.  
  1044.    CALL G#GetColor (Foreground%, Background%)
  1045.  
  1046. Circles and ellipses can be drawn with the Ellipse routine.
  1047. This is similar to the BASIC CIRCLE statement.  You specify the
  1048. center of the ellipse (X,Y), plus the X and Y radius values:
  1049.  
  1050.    CALL G#Ellipse (CenterX%, CenterY%, XRadius%, YRadius%)
  1051.  
  1052. A circle is an ellipse with a constant radius.  So, to draw a
  1053. circle, just set both radius values to the same value.
  1054.  
  1055. As well as the usual points, lines, and ellipses, PBWiz also
  1056. allows you to draw regular polygons: triangles, squares,
  1057. pentagons, hexagons, and so on.
  1058.  
  1059.    CALL G#Polygon (X%, Y%, Radius%, Vertices%, Angle!)
  1060.  
  1061. The X% and Y% values represent the coordinates of the center of
  1062. the polygon. The Radius% is the radius of the polygon (as if
  1063. you were fitting it into a circle).  Vertices% is the number of
  1064. angles (also the number of sides) for the polygon to have.
  1065. Angle! specifies the rotation of the polygon, and is specified
  1066. in radians.
  1067.  
  1068. When it comes to 256-color graphics modes, PowerBasic provides
  1069. no way to get or set the palette.  The 256-color modes allow
  1070. you to individually select the actual color to assign to a
  1071. given color number.  The modes are said to have 256 colors
  1072. because you may use up to 256 at a time (numbered 0 to 255).
  1073. You may choose the color for a given color number from a much
  1074. larger palette-- a bit under 262,144 colors, in fact.  Color 0
  1075. is black by default, for example, but there's no reason you
  1076. can't redefine it to mean pale turquoise.  In order to do this,
  1077. you must choose the proper color by blending three available
  1078. colors-- red, green, and blue-- in varying amounts.  Each may
  1079. have an intensity from 0 (off) to 63 (bright).  If you set both
  1080. RED and BLUE to 32 and GREEN to 0, for example, you'd get a
  1081. medium purple color.  It may take some experimentation to get
  1082. just the color you want.  Note that the intensities are finely
  1083. graded: you are unlikely to see any difference between an
  1084. intensity of 0 and an intensity of 3 in a color, for example.
  1085.  
  1086.                        Graphics Support                 page 24
  1087.  
  1088.  
  1089.  
  1090. The basic routines for getting and setting palette colors are
  1091. as follows.  Remember that the color number may be 0-255, while
  1092. each of the intensities can be 0-63.
  1093.  
  1094.    CALL GetPalRGB (ColorNr%, RedValue%, GreenValue%, BlueValu%)
  1095.    CALL SetPalRGB (ColorNr%, RedValue%, GreenValue%, BlueValu%)
  1096.  
  1097. If you're not concerned with the actual intensity levels, you
  1098. may find it more convenient to treat the color value as a unit,
  1099. stored in a long integer.  You can do that directly:
  1100.  
  1101.    ColorValue& = GetPal& (ColorNr%)
  1102.    CALL SetPal (ColorNr%, ColorValue%)
  1103.  
  1104. It's also easy enough to convert between the formats:
  1105.  
  1106.    ColorValue& = JoinRGB (RedValue%, GreenValue%, BlueValue%)
  1107.    CALL SplitRGB (ColorValue&, RedValu%, GreenValu%, BlueValu%)
  1108.  
  1109. The most obvious aspect of palettes is in the ability to select
  1110. a set of colors suited to a specific application.  When showing
  1111. a picture of the sea, you might want mostly blues and greens,
  1112. for instance.  There are other implications in the ability to
  1113. quickly change a color across the entire screen, however.  It
  1114. allows for simple animation, the ability to fade in or fade
  1115. out, and other interesting effects.  Let your imagination run
  1116. loose and experiment a little!  You'll be surprised by the
  1117. power of palette manipulation.
  1118.  
  1119.                        Keyboard Control                 page 25
  1120.  
  1121.  
  1122.  
  1123. The keyboard is not a particularly exciting or glamorous
  1124. device.  In fact, we tend to forget about it except when it
  1125. gets in the way.  Sometimes it's a hardware problem-- squishy
  1126. or clacking keys, or perhaps a commonly-used key placed in an
  1127. out-of-the-way location.  Then again, sometimes it's the
  1128. software that's the problem.  There are many aspects of
  1129. keyboard control, not all of which are necessarily related to
  1130. input.  This unit will let you handle the keyboard in ways you
  1131. may not have realized were possible.  Better yet, it can help
  1132. make keyboard control easier than the users of your programs
  1133. dreamed possible.
  1134.  
  1135. Let's start out with keyboard output.  Yep, not input--
  1136. output.  We can stuff up to 15 keys into the keyboard buffer.
  1137. Why would we ever want to do this? Perhaps to allow your
  1138. program to pop-up a TSR automatically, to start another program
  1139. after your program ends, or for creating key macros.  You can
  1140. enter extended key codes (such as function keys) by using
  1141. CHR$(0) before the scan code.
  1142.  
  1143.    CALL TypeIn (St$)
  1144.  
  1145. The usual keyboard action is somewhat sluggish.  We can make it
  1146. a lot crisper by changing the key repeat rate and the delay
  1147. before repeating begins.  This will work on ATs, but not PC/XT
  1148. systems.
  1149.  
  1150.    CALL SpeedKey (RepDelay%, RepRate%)
  1151.  
  1152. The delay may be 0-3 (1 by default):
  1153.  
  1154.    0      250 milliseconds
  1155.    1      500 milliseconds
  1156.    2      750 milliseconds
  1157.    3        1 second
  1158.  
  1159. The repeat rate may be 0-31 (11 by default).  The larger the
  1160. number, the slower the speed-- 0 is around 30 cps, and 31 is
  1161. around 2 cps.
  1162.  
  1163. I generally prefer to have the keyboard cranked up to full
  1164. speed, using RepDelay% and RepRate% both set to zero.  This may
  1165. be a bit too zippy for some people.  Experiment with it to see
  1166. what you like best.
  1167.  
  1168. Of course, there may be reasons to make keyboard repeat less
  1169. sensitive instead!  That might be a good idea in programs
  1170. written for small children, for example.  You can adjust the
  1171. keyboard equally well in either direction.
  1172.  
  1173.                        Keyboard Control                 page 26
  1174.  
  1175.  
  1176.  
  1177. PowerBASIC allows you to control one of the keys which can
  1178. interrupt your program, namely the Break key.  There's another
  1179. dangerous key which PBWiz allows you to control-- the PrtSc
  1180. (PrintScreen) key.  PrtSc may not seem like a hazard at first
  1181. glance, but if it's pressed by accident with no printer ready,
  1182. or in a graphics mode which PrtSc doesn't understand how to
  1183. print, the results can be pretty messy.  So, we let you turn it
  1184. off or back on:
  1185.  
  1186.    CALL SetPrtSc (PrtScON%)
  1187.  
  1188. Use 0 to turn it off or anything else to turn it back on.  If
  1189. you turn off PrtSc, you MUST remember to turn it back on again
  1190. before your program ends! Otherwise, an interrupt vector will
  1191. point into nowhere, causing probable chaos the next time PrtSc
  1192. is pressed.
  1193.  
  1194. Regardless of whether you've turned the PrtSc key off, you can
  1195. print the screen yourself just as if PrtSc had been pressed:
  1196.  
  1197.    CALL PrintScreen
  1198.  
  1199. Now here's a strange one for you.  When IBM brought out the
  1200. 101-key keyboard, called the "enhanced" keyboard, they did
  1201. something bizarre to the BIOS.  They still allowed old keyboard
  1202. calls to work, but they filtered out the new key codes so no
  1203. one would see them.  This made sure that no one would be able
  1204. to use the capabilities of the "enhanced" keyboard without
  1205. rewriting their programs.  So, the keyboard has been around for
  1206. years, and there are still few programs that even notice when
  1207. you press F11.  PowerBASIC v2.1 does not support the enhanced
  1208. keyboard at all.  Fortunately, PBWiz -does-.  You can find out
  1209. if an enhanced keyboard is installed with the KbdType%
  1210. function, which is in the Equipment unit.
  1211.  
  1212. If there is an enhanced keyboard installed, you can activate it
  1213. like so:
  1214.  
  1215.    CALL SetEnhKbd (Enhanced%)
  1216.  
  1217. With enhanced keyboard support activated, all key requests that
  1218. used the old services are translated to the new services.  So,
  1219. SetEnhKbd affects INKEY$ and other BASIC functions as well as
  1220. other PBWiz keyboard routines.  Note that you MUST deactivate
  1221. enhanced keyboard support before ending your program.
  1222. Otherwise, an interrupt vector will point into nowhere,
  1223. probably causing a crash on the next keypress!
  1224.  
  1225.                        Keyboard Control                 page 27
  1226.  
  1227.  
  1228.  
  1229. Speaking of INKEY$, I have a neat little function for you.  It
  1230. works like INKEY$, but it doesn't remove the key from the
  1231. keyboard buffer:
  1232.  
  1233.    ky$ = ScanKey$
  1234.  
  1235. How is this handy?  Well, let's suppose you're writing
  1236. something that will work like the DIR or TYPE commands in DOS.
  1237. It will display what may be a long listing, which you'd like to
  1238. be able to pause with Ctrl-S or cancel with Ctrl-C.  Trouble
  1239. is, if you use INKEY$ and the user was using "type ahead" to
  1240. store another command, you've just wiped out his command while
  1241. looking for those control codes.  With ScanKey$, you can check
  1242. the key nondestructively.
  1243.  
  1244. On the other hand, if you're about to request important input,
  1245. you may not want to chance having it answered from results of
  1246. the keyboard buffer-- could be that the user meant those keys
  1247. for another purpose.  In that case, it's a good approach to
  1248. clear out the keyboard buffer just before the input:
  1249.  
  1250.    CALL ClearKbd
  1251.  
  1252. No keyboard unit would be complete without a selection of
  1253. routines to check the shift states and get or set the keyboard
  1254. toggles.  Let's start with the toggles, which are so called
  1255. because they get toggled from one state to another:
  1256.  
  1257.     PRINT "Caps Lock  : ";
  1258.     IF CapsOn% THEN PRINT "ON" ELSE PRINT "OFF"
  1259.     PRINT "Insert     : ";
  1260.     IF InsertOn% THEN PRINT "ON" ELSE PRINT "OFF"
  1261.     PRINT "Num Lock   : ";
  1262.     IF NumOn% THEN PRINT "ON" ELSE PRINT "OFF"
  1263.     PRINT "Scroll Lock: ";
  1264.     IF ScrollOn% THEN PRINT "ON" ELSE PRINT "OFF"
  1265.  
  1266. You can also turn the toggles off or on.  It's courteous to
  1267. restore the original toggle states once you end your program,
  1268. so you might want to save the original values for that
  1269. purpose.  Then again, I guess that doesn't apply if your
  1270. program is designed for the specific purpose of setting the
  1271. toggles!
  1272.  
  1273.    CALL SetCaps (CapsLock%)
  1274.    CALL SetInsert (InsertKey%)
  1275.    CALL SetNum (NumLock%)
  1276.    CALL SetScroll (ScrollLock%)
  1277.  
  1278. Does anyone actually use ScrollLock for anything?  Just
  1279. wondering...
  1280.  
  1281.                        Keyboard Control                 page 28
  1282.  
  1283.  
  1284.  
  1285. The shift keys are unique in many respects.  They don't return
  1286. codes that can be detected with INKEY$ or stuffed into the
  1287. keyboard buffer; several can be pressed at the same time; and
  1288. they don't repeat.  You can detect 'em with PBWiz, at any rate:
  1289.  
  1290.    IF AltPress% THEN PRINT "An ALT key is pressed."
  1291.    IF CtrlPress% THEN PRINT "A CTRL key is pressed."
  1292.    IF ShiftPress% THEN PRINT "A SHIFT key is pressed."
  1293.  
  1294.    IF LAltPress% THEN PRINT "The LEFT ALT key is pressed."
  1295.    IF LCtrlPress% THEN PRINT "The LEFT CTRL key is pressed."
  1296.    IF LShiftPress% THEN PRINT "The LEFT SHIFT key is pressed."
  1297.  
  1298.    IF RAltPress% THEN PRINT "The RIGHT ALT key is pressed."
  1299.    IF RCtrlPress% THEN PRINT "The RIGHT CTRL key is pressed."
  1300.    IF RShiftPress% THEN PRINT "The RIGHT SHIFT key is pressed."
  1301.  
  1302. NOTE that LAltPress%, LCtrlPress%, RAltPress%, and RCtrlPress%
  1303. are ONLY available for enhanced keyboards.  They will not
  1304. return useful results on older keyboards.
  1305.  
  1306.                          Memory (EMS)                   page 29
  1307.  
  1308.  
  1309.  
  1310. This unit provides support for expanded memory.  It will work
  1311. with older EMS and EEMS drivers as well as the current EMS 4.0
  1312. standard, with the exception of one or two routines (as noted)
  1313. which take advantage of new capabilities.
  1314.  
  1315. Expanded memory may be present on any type of computer, from
  1316. 8088 PCs to 80486 ATs.  It usually comes as a hardware board
  1317. with a software driver for older machines; on ATs, it may be
  1318. implemented using only a software driver which converts it from
  1319. extended memory.  Drivers have also been written which make a
  1320. hard disk function as EMS memory.  This broad range of use
  1321. makes EMS support invaluable to programs which need extra
  1322. memory.  EMS can theoretically support up to 1 gigabyte of RAM,
  1323. although the documented limit as of v4.0 was only 32 megabytes.
  1324.  
  1325. Of course, the first thing you need to know is when dealing
  1326. with EMS is whether any EMS memory actually exists:
  1327.  
  1328.    IF EMSexists% THEN PRINT "EMS exists"
  1329.  
  1330. The EMS version may also be retrieved:
  1331.  
  1332.    CALL EMSver (MajorV%, MinorV%)
  1333.  
  1334. It would be a good idea to check the EMS version if you plan to
  1335. use any features which are only available as of EMS 4.0, such
  1336. as reallocation.
  1337.  
  1338. Besides a mere existence and version checks, you will want to
  1339. know how much EMS is available:
  1340.  
  1341.    PRINT "Total EMS installed: "; EMStotal%
  1342.    PRINT "Free EMS memory    : "; EMSfree%
  1343.  
  1344. If you actually tried the above two lines, you would get a pair
  1345. of values which don't seem to mean much.  The trick is to
  1346. multiply them by 16,384 to convert them to bytes.  This is
  1347. because EMS memory is always accessed in pages of 16k bytes
  1348. each.  Any time you are dealing with a quantity of EMS memory,
  1349. the quantity will be specified as a number of pages.
  1350.  
  1351.                          Memory (EMS)                   page 30
  1352.  
  1353.  
  1354.  
  1355. Before we get into the mechanics of accessing EMS memory, I'd
  1356. like to bring up an optional routine which can improve access
  1357. speed.  It should not be used if your program accesses EMS
  1358. using routines other than the ones included here in PBWiz.  If
  1359. you only use these EMS routines, though, you will find that it
  1360. makes some kinds of memory accesses faster.  Use 0 for normal
  1361. (slow) mode.  Do not use optimization if you are using more
  1362. than one EMS handle!
  1363.  
  1364.    CALL EMSopt (Fast%)
  1365.  
  1366. Ok, let's get down to the nitty gritty.  (Where did that
  1367. expression come from, anyway?!)  When you allocate EMS memory,
  1368. you specify the number of pages you want, which must be at
  1369. least 1.  If the allocation is successful, you are returned a
  1370. "handle" which you can use to access the allocated memory.
  1371. Otherwise, you get back an error code.
  1372.  
  1373.    CALL EMSopen (Pages%, Handle%, ErrCode%)
  1374.  
  1375. There are a limited number of handles available under some EMS
  1376. drivers.  The EMS spec, as of v4.0, allowed for a maximum of
  1377. 255 handles, and it's not unusual for a driver to support only
  1378. 20 or so.  Bearing in mind that some of these handles may be
  1379. used up by other applications, such as RAMdisks and caches,
  1380. this really doesn't allow much leeway.  Try to use as few
  1381. handles as possible!  You may well need to store multiple
  1382. values in different areas of the memory allocated for a single
  1383. handle, rather than allocating a new area of memory for each
  1384. value.
  1385.  
  1386. Suppose you find you need more memory than you first
  1387. allocated?  Or maybe less memory?  Well, provided EMS 4.0 or
  1388. later is in use, you can reallocate the block:
  1389.  
  1390.    CALL EMSresize (Handle%, Pages%, ErrCode%)
  1391.  
  1392. There is no way to reallocate memory under older versions of
  1393. EMS.  About the best you could do in that case would be to
  1394. allocate a new area of the desired size, copy over the relevant
  1395. data from the old area, and then deallocate the original area.
  1396. Of course, this assumes that there is enough memory available
  1397. to hold both areas, at least temporarily.
  1398.  
  1399. When you are finished using EMS, you must be sure to return the
  1400. memory you allocated to the system.  It is IMPORTANT to do this
  1401. before ending your program.  Otherwise, the memory you
  1402. allocated will be "lost" until the next time you boot the
  1403. computer.  Return the memory for each handle as follows:
  1404.  
  1405.    CALL EMSclose (Handle%)
  1406.  
  1407.                          Memory (EMS)                   page 31
  1408.  
  1409.  
  1410.  
  1411. Hmmmm... we've covered EMS detection, status info, allocating
  1412. memory, freeing memory, resizing memory... what's missing
  1413. here?  Ah!  We haven't discussed how to actually access the
  1414. memory!
  1415.  
  1416. Accessing EMS memory is quite simple, but it involves a couple
  1417. of steps.  EMS is mapped into a 64k block in the low area of
  1418. memory (the area under 1M, which can be directly accessed).
  1419. Since a page is 16k, the EMS block can hold up to four pages at
  1420. a time.  To transfer data between normal memory and EMS memory,
  1421. you must map the appropriate page(s) of EMS into the EMS
  1422. block.  This is done like so:
  1423.  
  1424.     CALL EMSmap (Handle%, PPage%, VPage%)
  1425.  
  1426. The PPage% is the physical page, that is, the page number
  1427. within the EMS block (0-3).  The VPage% is the virtual page,
  1428. which is the number of a page within the EMS memory associated
  1429. with Handle%.
  1430.  
  1431. Once you've mapped the desired virtual page into a physical
  1432. page, it can be accessed using standard PowerBASIC memory
  1433. commands, such PEEK$ and POKE$. First, set the segment:
  1434.  
  1435.    DEF SEG=EMSseg&
  1436.  
  1437. The offset within a page may be 0-16,383.  To get the
  1438. appropriate offset within the EMS block, you must add the
  1439. offset of the page itself.  This may be calculated as follows:
  1440.  
  1441.    DataOffset& = OffsetWithinPage% + PageNumber% * 16384&
  1442.  
  1443. One final note: for best compatibility, it would be good to
  1444. avoid using physical page 3 (the last 16k of the EMS block).
  1445. Some EMS drivers don't handle this page with complete accuracy,
  1446. for technical reasons I'm not going to get into right now.
  1447.  
  1448.                          Memory (XMS)                   page 32
  1449.  
  1450.  
  1451.  
  1452. This unit provides support for XMS extended memory.  It won't
  1453. work with extended memory unless an XMS driver is present.
  1454.  
  1455. Extended memory is only present in AT-class computers.  It is
  1456. not available on older PCs.  An XMS driver must also be used.
  1457. XMS drivers are included with MS-DOS 5.0 and Windows 3.0, among
  1458. other things, so this is not a major limitation.  XMS can
  1459. address a maximum of 64 megabytes.
  1460.  
  1461. The first thing to check is whether any XMS memory exists:
  1462.  
  1463.    IF XMSexists% THEN PRINT "XMS exists"
  1464.  
  1465. The XMS version may also be retrieved:
  1466.  
  1467.    CALL XMSver (MajorV%, MinorV%)
  1468.  
  1469. The amount of XMS memory available may be reported in either of
  1470. two different ways: total amount and largest available block.
  1471.  
  1472.    PRINT "Total XMS free    : "; XMStfree&
  1473.    PRINT "Largest free block: "; XMSlfree&
  1474.  
  1475. XMS memory is manipulated in terms of 1,024 byte blocks, so the
  1476. amount of free memory is reported in kilobytes.  Any time you
  1477. are dealing with a quantity of EMS memory, the quantity will be
  1478. specified as a number of 1K blocks, except as otherwise noted.
  1479.  
  1480.                          Memory (XMS)                   page 33
  1481.  
  1482.  
  1483.  
  1484. When you allocate XMS memory, you specify the number of
  1485. kilobytes that you want.  This may be 0-65535, in theory.
  1486. Dunno, I don't have 64M RAM <grin>. If the allocation is
  1487. successful, you are returned a "handle" which you can use to
  1488. access the allocated memory.  Otherwise, you get back an error
  1489. code.
  1490.  
  1491.    CALL XMSopen (KBytes&, Handle%, ErrCode%)
  1492.  
  1493. There are a limited number of handles available, although the
  1494. number can be controlled somewhat by a driver parameter.  It's
  1495. probably best to use as few handles as possible, to avoid
  1496. running out.  You may well want to store multiple values in
  1497. different areas of the memory allocated for a single handle,
  1498. rather than allocating a new area of memory for each value.
  1499.  
  1500. Suppose you find you need more memory than you first
  1501. allocated?  Or maybe less memory?  Just reallocate the block:
  1502.  
  1503.    CALL XMSresize (Handle%, KBytes&, ErrCode%)
  1504.  
  1505. When you are finished using XMS, you must be sure to return the
  1506. memory you allocated to the system.  It is IMPORTANT to do this
  1507. before ending your program.  Otherwise, the memory you
  1508. allocated will be "lost" until the next time you boot the
  1509. computer.  Return the memory for each handle as follows:
  1510.  
  1511.    CALL XMSclose (Handle%)
  1512.  
  1513. To transfer data between normal memory and XMS memory, you must
  1514. provide the segment and offset of the normal memory area (use
  1515. the PowerBASIC functions VARSEG and VARPTR to find the address
  1516. of a variable).  The position within XMS memory is specified as
  1517. a long-integer offset starting at zero.
  1518.  
  1519.     CALL XMSread (Handle%, Posn&, Bytes&, DSeg%, DOfs%)
  1520.  
  1521.     CALL XMSwrite (Handle%, Posn&, Bytes&, DSeg%, DOfs%)
  1522.  
  1523. Note that the Bytes& to transfer must be an EVEN NUMBER.  It is
  1524. not restricted to 64k, however, so you can transfer a great
  1525. deal of data with these routines.
  1526.  
  1527. The XMS spec guarantees a "reasonable" number of interrupt
  1528. windows during a transfer; however, it is possible that you
  1529. might experience some communications dropouts if you do large
  1530. transfers during high-speed telecommunications.  If you are
  1531. making heavy demands on the interrupt system, be sure to test
  1532. your program carefully under maximum load conditions.
  1533.  
  1534. A brief demo program is included which shows how to use XMS for
  1535. holding arrays.  The demo, XMSDEMO.BAS, shows a two-dimensional
  1536. long integer array.  This can be readily altered to support any
  1537. data type composed of an even number of bytes.
  1538.  
  1539.                          Mouse Support                  page 34
  1540.  
  1541.  
  1542.  
  1543. The mouse unit provides full-featured mouse support.  You can
  1544. see if a mouse is available and how many buttons it has, get
  1545. the cursor position (either the current position or the
  1546. position at the last press or release of a specified button),
  1547. set the cursor position, change the cursor, set the mouse
  1548. range, get hardware information about the mouse, and so on.
  1549.  
  1550. There are two unusual mouse modes to be aware of.  One is text
  1551. mode, which is mapped to a 640x200 virtual display.  So, to
  1552. convert the results to text format, you need to divide the
  1553. cursor position by eight and add one.  To convert from text
  1554. format, subtract one and multiply by eight.
  1555.  
  1556. The second unusual mode is 320x200 CGA mode, which is also
  1557. mapped to 640x200. To convert the coordinates to this mode,
  1558. divide X by two.  To convert from this mode, multiply the X
  1559. coordinate by two.
  1560.  
  1561. All other modes use the actual display coordinates instead of a
  1562. bizarro virtual screen.  Why the peculiar CGA and text modes?
  1563. Well, evidently Microsoft never thought there'd be any video
  1564. adapters besides MDA and CGA, and decided to create a single
  1565. virtual screen size that worked for all modes. Not a bad idea,
  1566. I guess, but rather shortsighted.  Oh well.
  1567.  
  1568. One other nuisance that you may run into is that the mouse
  1569. cursor can't be directly turned on or off.  A "cursor
  1570. visibility" count is maintained-- if the mouse cursor was
  1571. turned on twice, you'll need to turn it off twice before it
  1572. will actually disappear.
  1573.  
  1574. Before using the mouse, you must initialize it.  The
  1575. initialization routine also checks to make sure that a mouse is
  1576. installed and tells you how many buttons it has.  It's best to
  1577. initialize the mouse after setting the screen mode, so the
  1578. mouse driver understands what mode you're using.  Not all mouse
  1579. drivers support all screen modes, but you can reasonably expect
  1580. any current mouse driver to support MDA, CGA, EGA, and VGA.
  1581. Hercules graphics mode is rarely supported, as it must be set
  1582. through direct hardware access rather than the standard
  1583. techniques, so the mouse driver has little way of knowing that
  1584. you've changed the mode.
  1585.  
  1586. The mouse routines will work equally well with two-button or
  1587. three-button rodents.  The middle button functions will return
  1588. 0 with two-button mice.
  1589.  
  1590.                          Mouse Support                  page 35
  1591.  
  1592.  
  1593.  
  1594. I won't go into great detail on these routines, because they're
  1595. pretty much self-explanatory.  The mouse is a fairly easy
  1596. device to deal with.
  1597.  
  1598. You can initialize the mouse driver like so:
  1599.  
  1600.    Buttons% = MouseInit%
  1601.  
  1602. This returns the number of mouse buttons available.  If there
  1603. is no mouse, zero will be returned.  Initialize the mouse after
  1604. setting the screen mode.
  1605.  
  1606. You can make the mouse cursor visible or invisible.  It will
  1607. function just as well in either state.  See the previous page
  1608. for some quirks.
  1609.  
  1610.    CALL MouseShow       ' show the cursor
  1611.    CALL MouseHide       ' hide the cursor
  1612.  
  1613. There are many ways to get the mouse cursor position.  You can
  1614. get the current position, the position at which the mouse was
  1615. located when a particular button was pressed, or the position
  1616. when a button was released. If you choose a past position, you
  1617. can also find out how many presses or releases of the button
  1618. have taken place since you last checked.
  1619.  
  1620.    X% = MouseWhereX%                   ' current X coordinate
  1621.    Y% = MouseWhereY%                   ' current Y coordinate
  1622.  
  1623.    CALL MouseLClick (Count%, X%, Y%)   ' left presses & posn
  1624.    CALL MouseMClick (Count%, X%, Y%)   ' middle presses & posn
  1625.    CALL MouseRClick (Count%, X%, Y%)   ' right presses & posn
  1626.  
  1627.    CALL MouseLRelease (Count%, X%, Y%) ' left releases & posn
  1628.    CALL MouseMRelease (Count%, X%, Y%) ' middle releases & posn
  1629.    CALL MouseRRelease (Count%, X%, Y%) ' right releases & posn
  1630.  
  1631. If you'd prefer to find out which buttons are currently
  1632. pressed, no problem:
  1633.  
  1634.    Pressed% = MouseLButton%  ' whether left button is pressed
  1635.    Pressed% = MouseMButton%  ' whether middle button is pressed
  1636.    Pressed% = MouseRButton%  ' whether right button is pressed
  1637.  
  1638. Of course, you can also set the cursor location:
  1639.  
  1640.    CALL MouseLocate (X%, Y%)   ' set the mouse cursor position
  1641.  
  1642. The mouse cursor range can be restricted to a given area of the
  1643. screen.  This area is expressed by giving the upper left corner
  1644. and lower right corner of the rectangular area to which to
  1645. restrict the cursor.
  1646.  
  1647.    CALL MouseWindow (X1%, Y1%, X2%, Y2%)
  1648.  
  1649.                          Mouse Support                  page 36
  1650.  
  1651.  
  1652.  
  1653. There are a variety of cursor shapes available for graphics
  1654. mode:
  1655.  
  1656.     0    hourglass ("please wait, program is working" symbol)
  1657.     1    pointing arrow (default)
  1658.     2    pointing hand
  1659.     3    crosshair
  1660.     4    target (box in a box)
  1661.     5    grabbing hand
  1662.  
  1663. If you have ideas for more, let me know and I'll see what I can
  1664. do.  Cursor shapes are not unduly difficult to define.
  1665.  
  1666. The cursor image is set like so:
  1667.  
  1668.    CALL MouseCursorG (CursorNr%)
  1669.  
  1670.                             Strings                     page 37
  1671.  
  1672.  
  1673.  
  1674. One of the true strengths of BASIC in general, and PowerBasic
  1675. in particular, is its powerful string handling capability.  I'd
  1676. be remiss if I didn't provide extensions which improve it even
  1677. further.
  1678.  
  1679. The simplest of the PBWiz string routines may seem somewhat
  1680. whimsical, but it has proven useful to me on several occasions
  1681. in the past.  It reverses the order of characters in a string.
  1682.  
  1683.    CALL Reverse (St$)
  1684.  
  1685. One of the places this has come in useful is in searching a
  1686. string from the end-- a reverse INSTR routine:
  1687.  
  1688.    CALL RInstr (MainSt$, SubSt$, Posn%)
  1689.  
  1690. Rather than returning the first occurrence of a substring
  1691. within a main string, it returns the last occurrence.  Another
  1692. handy string search allows you to search for various types of
  1693. characters, rather than a specific substring:
  1694.  
  1695.    CALL TInstr (MainSt$, Types%, Posn%)
  1696.  
  1697. The type(s) may be specified using any combination of the
  1698. following.  Just add them together.
  1699.  
  1700.     1    alphabetic
  1701.     2    numeric
  1702.     4    symbolic
  1703.     8    control
  1704.    16    graphics
  1705.    32    space
  1706.  
  1707. Since you can search for any specific types, you can also
  1708. readily invert the search to look for any characters that are
  1709. NOT of a given type or types:
  1710.  
  1711.    Types% = NOT Types%
  1712.  
  1713. This gives you complete control.  A typical use for TInstr
  1714. might be to clean up user input and make sure that it's valid.
  1715. It's also good for parsing.
  1716.  
  1717.                             Strings                     page 38
  1718.  
  1719.  
  1720.  
  1721. Another routine that is useful for cleaning up and parsing user
  1722. input is called Crunch.  It allows you to eliminate adjacent
  1723. duplicates of a character or list of characters.  One use for
  1724. this, for instance, would be to eliminate repeated spaces,
  1725. converting an input string from "*.*    *.BAK  /B" to a more
  1726. manageable "*.* *.BAK /B".
  1727.  
  1728.    Result$ = Crunch$(St$, CharList$)
  1729.  
  1730. There are a pair of routines that you'll find valuable if you
  1731. need to check the validity of a string.  These are designed to
  1732. be compatible with the Xmodem and Ymodem file transfer
  1733. protocols, so you can use them for error checking in
  1734. telecommunications as well.
  1735.  
  1736.    Chk% = CheckSum% (St$)
  1737.  
  1738.    CALL CRC16 (St$, HiCRC%, LoCRC%)
  1739.  
  1740. Another pair of string routines provide a simple encryption
  1741. and decryption system for text.  The method used is not
  1742. particularly secure but are very fast and will be adequate for
  1743. many purposes.  As always, it helps to use a long and/or
  1744. complex password.
  1745.  
  1746.    CALL Cipher (St$, Password$)
  1747.    CALL CipherP (St$, Password$)
  1748.  
  1749. Both of these routines will encipher text on the first
  1750. run-through and decipher on the second, so you can use the same
  1751. routine either to encrypt or decrypt a message.  They are
  1752. different in one respect: the encrypted result of Cipher may
  1753. contain control characters, so it can't be used in a plain
  1754. sequential-access file.  The CipherP routine does not allow use
  1755. of extended ASCII characters (CHR$(128) - CHR$(255)), as it
  1756. sets the high bit on each character after encrypting it.  This
  1757. causes the results of CipherP to be printable (and useful in
  1758. sequential-access files), although they will look very strange.
  1759.  
  1760. The strings are encrypted (or decrypted) in place.  This
  1761. provides a certain extra measure of security for encryption--
  1762. the original plaintext strings are not left floating around in
  1763. memory where someone might see them.
  1764.  
  1765. One function is as much a file manipulation routine as it is a
  1766. string function.  It allows you to compare a file name to a
  1767. file pattern (which may contain wildcards) to see if they
  1768. match.  Only bare filespecs are supported-- you may not use
  1769. drive or path specifications in the names.
  1770.  
  1771.    IF MatchFile% (Pattern$, Filename$) THEN PRINT Filename$
  1772.  
  1773.                             Strings                     page 39
  1774.  
  1775.  
  1776.  
  1777. The MatchFile function can be used in creating your own
  1778. DOS-style utilities: DIR, COPY, and so forth.  Besides the
  1779. usual "accept file if it matches" approach, it can also be used
  1780. to implement the opposite: "exclude file if it matches."  This
  1781. gives you more flexibility than DOS itself supplies.
  1782.  
  1783. The PowerBASIC compiler provides a very nice function called
  1784. Extract$.  This allows you to retrieve a substring running from
  1785. the left side of a main string to a specified character
  1786. delimiter.  Not bad, but it might be handy to be able to grab a
  1787. numbered substring from any part of a main string, and to be
  1788. able to use a substring delimiter.  For instance, you might
  1789. load a record from a database which contains an address, where
  1790. each line is delimited by a carriage return and linefeed.
  1791. Rather than mucking around with Extract$, which really wasn't
  1792. designed with that sort of thing in mind, you'd do better to
  1793. use the PBWiz function called DelimExtract$:
  1794.  
  1795.    SubSt$ = DelimExtract$(St$, Delimiter$, Index%)
  1796.  
  1797. The index starts at 1 with the first substring.  If you choose
  1798. the index of a substring which doesn't actually exist, a null
  1799. string will be returned.
  1800.  
  1801.                         Text-mode Video                 page 40
  1802.  
  1803.  
  1804.  
  1805. The graphical interface has become a "sexy" thing to have
  1806. these days, but there are still many good reasons to work in
  1807. text mode.  It's relatively fast, it works on all monitors,
  1808. and it's often the most appropriate choice.  Graphics is
  1809. overkill for many applications.  Besides, you can't redirect
  1810. graphics to a file or to the printer.
  1811.  
  1812. Come to think of it, text displayed by the PRINT statement
  1813. can't be redirected either.  Fortunately, we can fix that.
  1814. All it takes is sending the output through DOS:
  1815.  
  1816.    CALL DOSPrint (St$)   ' print to the current output device
  1817.  
  1818. A nice thing about DOS output is that ANSI display codes will
  1819. work if you have an ANSI driver (such as ANSI.SYS) installed.
  1820. If you're working with existing text, such as captured output
  1821. from a BBS or from an ANSI art program like TheDraw, you can
  1822. just use the DOSPrint routine to handle it.  If you're looking
  1823. to do your own ANSI output, though, there's an easier way than
  1824. looking up the individual codes and sending 'em out one at a
  1825. time.  The following routines send the appropriate ANSI codes
  1826. to the current DOS output device:
  1827.  
  1828.    CALL DOSCls                      ' clear the screen
  1829.    CALL DOSColor (Fore%, Back%)     ' set the screen colors
  1830.    CALL DOSLocate (Row%, Column%)   ' set the cursor position
  1831.  
  1832. The primary advantage of DOS output is that it can be
  1833. intercepted.  It normally goes to the screen, but you can
  1834. redirect it to a file, printer, or comm port, among other
  1835. things.  You can also be sure that DOS output will work
  1836. reasonably in a multitasking environment like DESQview or
  1837. Windows, instead of messing up the screen.  The disadvantage
  1838. is that DOS output is fairly slow.  It's great for command
  1839. line utilities, but not if you plan to do any fancy screen
  1840. work.  For that, you probably want direct-access techniques.
  1841.  
  1842. The direct screen access provided by PBWiz is rude, crude...
  1843. and faster than a greased euphemism.  It can't be redirected,
  1844. doesn't handle control codes, and won't even update the cursor
  1845. position.  In return for this lack of amenities, it gives you
  1846. two very valuable things: complete control and raw speed.
  1847.  
  1848.                         Text-mode Video                 page 41
  1849.  
  1850.  
  1851.  
  1852. The direct-access replacement for PRINT works like so:
  1853.  
  1854.    CALL XQPrint (St$, Row%, Column%, Attr%)
  1855.  
  1856. Now, you might guess that St$ is the text to print, and Row%
  1857. and Column% are where to print it.  The Attr% may seem a bit
  1858. more opaque.  The Attr% is the color to use-- actually, both
  1859. the foreground and background colors.  These are combined into
  1860. a single value because that's the way the display controller
  1861. wants to see it.  Remember, the emphasis here is on speed, not
  1862. necessarily convenience!  So how do you calculate an attribute
  1863. given the foreground and background colors?
  1864.  
  1865.    Attr% = CalcAttr% (Fore%, Back%)
  1866.  
  1867. An Attr% value is never less than 0 or greater than 255-- it
  1868. will fit into a single byte.  That may be useful to know for
  1869. storage purposes.  In any event, keep the Attr% in mind,
  1870. because we'll be seeing more of it in the future.  By the way,
  1871. you can unpack an attribute into foreground and background
  1872. colors too, if need be:
  1873.  
  1874.    CALL UnCalcAttr (Attr%, Fore%, Back%)
  1875.  
  1876. In addition to XQPrint, there is a routine which allows you to
  1877. overlay existing text rather than replacing it.  It skips
  1878. blank spaces rather than putting them on the display.
  1879.  
  1880.    CALL XQPrintOver (St$, Row%, Column%, Attr%)
  1881.  
  1882. The direct-access routines allow for one, and only one, option
  1883. which might slow them down.  The IBM CGA and some clone CGAs
  1884. flicker horribly if you access their display memory directly.
  1885. There is a software fix, but it slows the display down
  1886. tremendously.  Still, if your program is run on such a CGA,
  1887. you'll want to stop the flickering.  This may be done so:
  1888.  
  1889.    CALL AntiSnow (Slow%)   ' any nonzero value to stop flicker
  1890.  
  1891. You should provide a command-line switch or configuration
  1892. option to force anti-flicker support.  Don't do it by default,
  1893. as the slowdown is very noticeable.
  1894.  
  1895. The "menu" approach has become the standard way of allowing a
  1896. user to choose between various program options.  There are
  1897. many ways of designing a menu, but they almost always require
  1898. a "highlight" to show the current choice.  This highlight is
  1899. generally handled by changing the color of the chosen item:
  1900.  
  1901.    CALL ReColorArea (TopRow%, LftCol%, BotRow%, RtCol%, Attr%)
  1902.  
  1903.                         Text-mode Video                 page 42
  1904.  
  1905.  
  1906.  
  1907. Pop-up windows have become ubiquitous.  Naturally, PBWiz
  1908. supports them too:
  1909.  
  1910.    CALL PopWindow (TRow%, LCol%, BRow%, RCol%, Frame%,
  1911.       Attr%, Grow%, Shade%, TFore%, Title$)   ' use one line!
  1912.  
  1913. The first four parameters specify the upper left corner and
  1914. lower right corner of the window.  The window frame, if any,
  1915. is created just outside these coordinates.  If you choose a
  1916. shadow for a 3D effect, that will extend further outside the
  1917. window.  Keep this in mind if you want to save the part of the
  1918. screen under the window... but we'll get to that!  Let's see
  1919. what options are available for the frame type:
  1920.     0   no frame
  1921.     1   single lines
  1922.     2   double lines
  1923.     3   single horizontal, double vertical lines
  1924.     4   double horizontal, single vertical lines
  1925.     5   block graphic lines
  1926.  
  1927. These are the available shadows:
  1928.    -3   transparent shadow on the right
  1929.    -2   transparent shadow on the left
  1930.    -1   solid black shadow on the left
  1931.     0   no shadow
  1932.    1+   shadow attribute (use CalcAttr) for a colored shadow
  1933.  
  1934. Options for growing windows are as follows:
  1935.    -1   grow as fast as possible
  1936.     0   pop onto the screen
  1937.    1+   grow with a specified delay in milliseconds
  1938.         (15 works well for me)
  1939.  
  1940. The TFore% parameter is the foreground color to use for the
  1941. title (Title$), if any (use "" for no title).
  1942.  
  1943. It is worth noting that the "milliseconds" value is only
  1944. rather approximate.  The delay is based on the video card, and
  1945. is fairly similar on any computer system, but there will be a
  1946. noticeable difference between (say) an XT with a CGA and a 486
  1947. with a VGA.  Still, it's a useful delay-- if not really
  1948. precise, it's at least fairly accurate, and it has a fine
  1949. resolution.  You can access this delay yourself:
  1950.  
  1951.    CALL DelayV (MilliSeconds%)
  1952.  
  1953.                         Text-mode Video                 page 43
  1954.  
  1955.  
  1956.  
  1957. I mentioned screen saves a bit earlier.  With PBWiz, you can
  1958. save any part of a screen, and restore it later to the same
  1959. place or an entirely different area.  This also offers the
  1960. possibility of creating a screen image, storing it in a file,
  1961. and reading it into your program, among other things.  It
  1962. works like this:
  1963.  
  1964.    Scr$ = ScreenSave$ (TopRow%, LftCol%, BotRow%, RtCol%)
  1965.  
  1966.    CALL ScreenRestore (Scr$, TopRow%, LftCol%)
  1967.  
  1968. It takes about 4K to store a full 80x25 text screen.  The
  1969. exact calculation is Bytes = Rows * Columns * 2 + 2, if you
  1970. care to figure it out yourself. You can also take advantage of
  1971. a PBWiz routine to handle it:
  1972.  
  1973.    Bytes% = CalcSize% (TopRow%, LftCol%, BotRow%, RtCol%) + 2
  1974.  
  1975. Finally, we are left with a series of routines which let you
  1976. scroll (or clear) any part of the screen:
  1977.  
  1978.    CALL ScrollDown (TRow%, LCol%, BRow%, RCol%, Times%, Attr%)
  1979.    CALL ScrollLeft (TRow%, LCol%, BRow%, RCol%, Times%, Attr%)
  1980.    CALL ScrollRight (TRow%, LCol%, BRow%, RCol%, Times%, Attr%)
  1981.    CALL ScrollUp (TRow%, LCol%, BRow%, RCol%, Times%, Attr%)
  1982.  
  1983. If you attempt to scroll zero times, or more times than there
  1984. are rows (or columns, depending on which way you scroll), the
  1985. specified area of the screen will be cleared.  The Attr% gives
  1986. the color to use on the cleared area of the screen.
  1987.  
  1988.                             Credits                     page 44
  1989.  
  1990.  
  1991.  
  1992. I'd like to thank Dave Navarro for letting me in on the world
  1993. of PowerBASIC.  His help has been most valuable to me in many
  1994. respects.  Without him, this library would have taken much
  1995. longer to get off the ground or would perhaps not even exist.
  1996.  
  1997. I would also like to thank Spectra, publishers of PowerBASIC,
  1998. for sending me the evaluation copy of PowerBASIC which led to
  1999. my decision to write this library.  I have also found their
  2000. tech support to be outstanding and of great assistance.
  2001.  
  2002. My thanks also to the good people who have registered PBWiz.
  2003. You're the ones that make PBWiz happen-- without your support,
  2004. this library dies.
  2005.  
  2006.